package com.mrogrape.gui;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JPanel;

import com.mrogrape.gui.event.PageChangeListener;
import com.mrogrape.gui.event.PageData;
import com.mrogrape.util.StringUtils;

/**
 * PageContainer是一个容器，采用栈策略来实现类似CardLayout的功能。
 * 
 * <p>
 * PageContainer中可以自动管理它的“页面”。它提供了一个按钮面板来实现不同页面之
 * 间的切换。切换的策略由PageContainer的子容器Page的实现来确定。
 * 这需要这些页面遵守以下的规约：
 * <ul>
 *    <li>继承自Page类并实现全部方法。
 *    <li>需要被同一个PageContainer管理的全部页面都需要在基础包（BasePackage)下
 *    <li>方法getPageName()的返回值和类名相同
 *    <li>提供一个默认构造器
 * </ul>
 * 将basePackageName属性设置为遵守以上规约的实现类的包名即可实现对这些页面的自动管理。
 * 
 * <p>
 * 此外，如果页面需要分类，则可以在basepackage下创建子包。指定页面时只需在页面名前
 * 加“包名.”即可。如不指定BasePackage或指定为空，则通过Page实现的onNext处理方法的
 * 返回值指定下一个页面时，需要指定类的完全修饰名。
 * 
 * @author Ice Spring
 * @version 1.1.0 201211121132u2
 */
public class PageContainer extends JPanel {

	/** serialVersionUID */
	private static final long serialVersionUID = 1L;

	// ACTIONS
	private static final int BACK_ACTION = 1;
	private static final int NEXT_ACTION = 2;
	private static final int FINISH_ACTION = 3;
	private static final int CANCEL_ACTION = 4;

	/**
	 * 构造一个页面容器。
	 * 
	 * @param firstPageName 第一个页面名
	 * @param pageData 页面数据
	 */
	public PageContainer(String firstPageName, PageData pageData){

		this(null, firstPageName, pageData);
	}

	/**
	 * 构造一个页面容器。
	 * 
	 * @param basePackageName 当前容器所管理的页面所在的基本package。
	 * @param firstPageName 第一个页面的名称。
	 * @param pageData 页面传输用的数据
	 */
	public PageContainer(String basePackageName, String firstPageName, PageData pageData){

		if(pageData == null||StringUtils.isEmpty(firstPageName)){
			throw new NullPointerException("Parameters FirstPageName and PageData can not be empty");
		}

		this.basePackageName = StringUtils.nullToEmpty(basePackageName);
		this.firstPageName = firstPageName;
		this.pageData = pageData;

		this.init();
	}

	/**
	 * 如果“Next”按钮可用，则触发一次Next操作
	 */
	public void next(){

		if(this.buttonPanel.isButtonEnable(PageButtonPanel.NEXT_BUTTON)
				&&this.buttonPanel.isButtonVisable(PageButtonPanel.NEXT_BUTTON)){

			this.onNextClick(null);
		}
	}

	/**
	 * 如果“back”按钮可用，则触发一次back操作
	 */
	public void back(){

		if(this.buttonPanel.isButtonEnable(PageButtonPanel.BACK_BUTTON)
				&&this.buttonPanel.isButtonVisable(PageButtonPanel.BACK_BUTTON)){

			this.onBackClick(null);
		}
	}

	/**
	 * 如果“finish”按钮可用，则触发一次finish操作
	 */
	public void finish(){

		if(this.buttonPanel.isButtonEnable(PageButtonPanel.FINISH_BUTTON)
				&&this.buttonPanel.isButtonVisable(PageButtonPanel.FINISH_BUTTON)){

			this.onFinishClick(null);
		}
	}

	/**
	 * 如果“cancel”按钮可用，则触发一次cancel操作
	 */
	public void cancel(){

		if(this.buttonPanel.isButtonEnable(PageButtonPanel.CANCEL_BUTTON)
				&&this.buttonPanel.isButtonVisable(PageButtonPanel.CANCEL_BUTTON)){

			this.onCancelClick(null);
		}
	}

	/**
	 * 添加一个页面更改监听器。
	 * 
	 * @param l
	 */
	public void addPageChangeListener(PageChangeListener l){

		this.listeners.add(l);
	}

	/**
	 * 移除一个页面更改监听器。
	 * 
	 * @param l
	 */
	public void removePageChangeListener(PageChangeListener l){

		this.listeners.remove(l);
	}

	/**
	 * 释放所有页面的资源，回到第一个页面。
	 */
	public void dispose(){

		// 移除当前页面
		this.remove(this.getCurrentPage());

		// 添加第一个页面
		this.add(this.firstPage);

		// 循环处理
		for(int level = this.levelMap.size(); level > 0; level--){

			// 获取当前顶层页面
			Page page = this.levelMap.get(level);

			if(level >= 2){

				// 移除顶层页面
				this.levelMap.remove(level);

				// 获取上一个页面
				Page lastPage = this.levelMap.get(level - 1);

				// 如果前后画面不一致或画面不循环使用
				if(!StringUtils.isEquals(page.getClass().getName(), lastPage.getClass().getName()) 
						|| !lastPage.isRecycle()){

					// 释放资源
					page.onDispose();
				}
			}

			// 重置按钮面板
			this.setButtonPanelStatus(this.firstPage);
		}

		// 通知页面重绘
		this.validate();
		this.repaint();
	}

	/**
	 * 获取当前正在显示的面板。
	 * 
	 * @return 当前正在显示的面板
	 */
	public Page getCurrentPage(){

		return this.levelMap.get(this.levelMap.size());
	}

	/**
	 * 获取页面传输数据。
	 * 
	 * @return 页面传输用数据。
	 */
	public PageData getPageData(){

		return this.pageData;
	}

	private void init(){

		// 初始化对象
		this.buttonPanel = new DefaultButtonPanel();
		this.firstPage = this.instantPage(this.firstPageName);
		this.pageData.setContainer(this);
		this.pageData.setFirstPageClassName(this.getPageClassName(this.firstPageName));

		// 初始化UI
		this.setLayout(new BorderLayout());
		this.add(this.buttonPanel, BorderLayout.SOUTH);
		this.add(this.firstPage, BorderLayout.CENTER);
		this.setButtonPanelStatus(this.firstPage);

		// 添加事件
		this.buttonPanel.addButtonActionListener(PageButtonPanel.BACK_BUTTON,
				new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {

				PageContainer.this.onBackClick(e);
			}
		});
		this.buttonPanel.addButtonActionListener(PageButtonPanel.NEXT_BUTTON,
				new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {

				PageContainer.this.onNextClick(e);
			}
		});
		this.buttonPanel.addButtonActionListener(PageButtonPanel.FINISH_BUTTON,
				new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {

				PageContainer.this.onFinishClick(e);
			}
		});
		this.buttonPanel.addButtonActionListener(PageButtonPanel.CANCEL_BUTTON,
				new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {

				PageContainer.this.onCancelClick(e);
			}
		});

		// 初始化数据
		this.levelMap.put(1, this.firstPage);
	}

	private void onBackClick(ActionEvent e){

		// 触发当前页面的onBack事件
		Page page = this.getCurrentPage();
		boolean continueFlag = page.onBack(this.pageData);

		// 如果续行标志为false，则返回
		if(!continueFlag) {
			return;
		}

		// 返回前一个页面
		this.levelDown();

		// 通知监听器事件发生
		this.fireListeners(PageContainer.BACK_ACTION);

	}

	private void onNextClick(ActionEvent e){

		Page page = this.getCurrentPage();
		String nextName = page.onNext(this.pageData);

		// 如果返回空，则不继续执行
		if(StringUtils.isEmpty(nextName)){
			return;
		}

		// 判断下一个页面是否和当前页面名称相同
		if(StringUtils.isEquals(page.getClass().getName(), nextName)) {

			// 如果当前页面是循环使用
			if(page.isRecycle()){

				page.onCreate(this.pageData);
				this.levelUp(page);
			}
		}

		// 否则创建新的页面
		Page nextPage = this.instantPage(nextName);
		this.levelUp(nextPage);

		// 通知监听器事件发生
		this.fireListeners(PageContainer.NEXT_ACTION);

	}

	private void onFinishClick(ActionEvent e){

		Page page = this.getCurrentPage();
		boolean flag = page.onFinish(this.pageData);

		if(!flag) {
			return;
		}

		// 释放资源，回到第一个页面
		this.dispose();

		// 通知监听器事件发生
		this.fireListeners(PageContainer.FINISH_ACTION);

	}

	private void onCancelClick(ActionEvent e){

		Page page = this.getCurrentPage();
		boolean flag = page.onCancel(this.pageData);

		if(!flag) {
			return;
		}

		// 释放资源，回到第一个页面
		this.dispose();

		// 通知监听器事件发生
		this.fireListeners(PageContainer.CANCEL_ACTION);
	}

	/**
	 * 实例化一个页面对象。
	 * 
	 * @param name 页面名
	 * @return
	 */
	private Page instantPage(String name){

		Page page = null;
		try {
			page = (Page) Class.forName(this.getPageClassName(name)).newInstance();
			page.onCreate(this.pageData);
		} catch (InstantiationException e) {
			throw new RuntimeException("Can not create pane instance :" + this.getPageClassName(name)
					+ "\n" + e.getMessage());
		} catch (IllegalAccessException e) {
			throw new RuntimeException("Can not create pane instance :" + this.getPageClassName(name)
					+ "\n" + e.getMessage());
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("Can not create pane instance :" + this.getPageClassName(name)
					+ "\n" + e.getMessage());
		}
		return page;
	}

	/**
	 * 获取要创建的页面的类的完整类名。
	 * 
	 * @param simpleName 简单类名
	 * @return 完整类名
	 */
	private String getPageClassName(String simpleName){

		return this.basePackageName + "." + simpleName;
	}

	/**
	 * 显示下一个页面
	 * 
	 * @param page
	 */
	private void levelUp(Page page){

		int level = this.levelMap.size();
		if(level == 1){
			this.buttonPanel.setButtonEnable(PageButtonPanel.BACK_BUTTON, true);
		}

		// 从显示区移除当前页面
		this.remove(this.getCurrentPage());
		this.add(page, BorderLayout.CENTER);
		this.levelMap.put(level + 1, page);
		this.setButtonPanelStatus(page);

		// 通知页面重绘
		this.validate();
		this.repaint();
	}

	/**
	 * 显示上一个页面
	 */
	private void levelDown(){

		// 获取当前页面数量
		int level = this.levelMap.size();

		// 如果只有一页，则不进行任何操作
		if(level <= 1) {
			return;
		}

		// 获取页面
		Page page = this.getCurrentPage();

		// 移除旧页面
		this.remove(this.getCurrentPage());
		this.levelMap.remove(level);

		// 添加新页面并初始化
		Page currentPage = this.getCurrentPage();
		this.add(currentPage);
		this.setButtonPanelStatus(currentPage);

		// 如果前后画面不一致或画面不循环使用
		if(!StringUtils.isEquals(page.getClass().getName(), currentPage.getClass().getName()) 
				|| !currentPage.isRecycle()){

			// 释放资源
			page.onDispose();
		}

		if (level == 2){

			this.buttonPanel.setButtonEnable(PageButtonPanel.BACK_BUTTON, false);
		}

		// 通知页面重绘
		this.validate();
		this.repaint();
	}

	/**
	 * 设置按钮面板的状态
	 */
	private void setButtonPanelStatus(Page page){

		// 如果是第一个页面
		if(page != this.firstPage){

			this.buttonPanel.setButtonEnable(PageButtonPanel.BACK_BUTTON, page.isCanBack());
		}
		else{

			this.buttonPanel.setButtonEnable(PageButtonPanel.BACK_BUTTON, false);
		}

		this.buttonPanel.setButtonEnable(PageButtonPanel.NEXT_BUTTON, page.isHasNext());
		this.buttonPanel.setButtonEnable(PageButtonPanel.FINISH_BUTTON, page.isFinishable());
		this.buttonPanel.setButtonEnable(PageButtonPanel.CANCEL_BUTTON, page.isCancelable());
	}

	/**
	 * 通知监听器对应的事件已更改。
	 * 
	 * @param action 动作名
	 */
	private void fireListeners(int action){

		for(PageChangeListener l : this.listeners){

			switch(action){
				case BACK_ACTION:
					l.onBack(this.pageData);
					break;
				case NEXT_ACTION:
					l.onNext(this.pageData);
					break;
				case FINISH_ACTION:
					l.onFinish(this.pageData);
					break;
				case CANCEL_ACTION:
					l.onCancel(this.pageData);
					break;
				default:
					break;
			}
		}
	}

	private String basePackageName;
	private String firstPageName;
	private PageData pageData;
	private PageButtonPanel buttonPanel;

	private Page firstPage;
	private Map<Integer, Page> levelMap = new HashMap<Integer, Page>();
	private List<PageChangeListener> listeners = Collections.synchronizedList(new ArrayList<PageChangeListener>());
}


class DefaultButtonPanel extends PageButtonPanel {

	/** serialVersionUID */
	private static final long serialVersionUID = 1L;

	public DefaultButtonPanel(){

		this.initComponents();
	}

	@Override
	public void addButtonActionListener(int button, ActionListener l) {

		switch(button){
			case PageButtonPanel.BACK_BUTTON:
				this.backBtn.addActionListener(l);
				break;
			case PageButtonPanel.NEXT_BUTTON:
				this.nextBtn.addActionListener(l);
				break;
			case PageButtonPanel.FINISH_BUTTON:
				this.finishBtn.addActionListener(l);
				break;
			case PageButtonPanel.CANCEL_BUTTON:
				this.cancelBtn.addActionListener(l);
				break;
			default:
				throw new IllegalArgumentException("Wrong Button Type.");
		}
	}

	@Override
	public String getButtonText(int button) {

		switch(button){
			case PageButtonPanel.BACK_BUTTON:
				return this.backBtn.getText();
			case PageButtonPanel.NEXT_BUTTON:
				return this.nextBtn.getText();
			case PageButtonPanel.FINISH_BUTTON:
				return this.finishBtn.getText();
			case PageButtonPanel.CANCEL_BUTTON:
				return this.cancelBtn.getText();
			default:
				throw new IllegalArgumentException("Wrong Button Type.");
		}
	}

	@Override
	public boolean isButtonEnable(int button) {

		switch(button){
			case PageButtonPanel.BACK_BUTTON:
				return this.backBtn.isEnabled();
			case PageButtonPanel.NEXT_BUTTON:
				return this.nextBtn.isEnabled();
			case PageButtonPanel.FINISH_BUTTON:
				return this.finishBtn.isEnabled();
			case PageButtonPanel.CANCEL_BUTTON:
				return this.cancelBtn.isEnabled();
			default:
				throw new IllegalArgumentException("Wrong Button Type.");
		}
	}

	@Override
	public boolean isButtonVisable(int button) {

		switch(button){
			case PageButtonPanel.BACK_BUTTON:
				return this.backBtn.isVisible();
			case PageButtonPanel.NEXT_BUTTON:
				return this.nextBtn.isVisible();
			case PageButtonPanel.FINISH_BUTTON:
				return this.finishBtn.isVisible();
			case PageButtonPanel.CANCEL_BUTTON:
				return this.cancelBtn.isVisible();
			default:
				throw new IllegalArgumentException("Wrong Button Type.");
		}
	}

	@Override
	public void removeBtnActionListener(int button, ActionListener al) {

		switch(button){
			case PageButtonPanel.BACK_BUTTON:
				this.backBtn.removeActionListener(al);
				break;
			case PageButtonPanel.NEXT_BUTTON:
				this.nextBtn.removeActionListener(al);
				break;
			case PageButtonPanel.FINISH_BUTTON:
				this.finishBtn.removeActionListener(al);
				break;
			case PageButtonPanel.CANCEL_BUTTON:
				this.cancelBtn.removeActionListener(al);
				break;
			default:
				throw new IllegalArgumentException("Wrong Button Type.");
		}
	}

	@Override
	public void setButtonText(String text, int button) {

		switch(button){
			case PageButtonPanel.BACK_BUTTON:
				this.backBtn.setText(text);
				break;
			case PageButtonPanel.NEXT_BUTTON:
				this.nextBtn.setText(text);
				break;
			case PageButtonPanel.FINISH_BUTTON:
				this.finishBtn.setText(text);
				break;
			case PageButtonPanel.CANCEL_BUTTON:
				this.cancelBtn.setText(text);
				break;
			default:
				throw new IllegalArgumentException("Wrong Button Type.");
		}
	}

	@Override
	public void setButtonEnable(int button, boolean enable) {

		switch(button){
			case PageButtonPanel.BACK_BUTTON:
				this.backBtn.setEnabled(enable);
				break;
			case PageButtonPanel.NEXT_BUTTON:
				this.nextBtn.setEnabled(enable);
				break;
			case PageButtonPanel.FINISH_BUTTON:
				this.finishBtn.setEnabled(enable);
				break;
			case PageButtonPanel.CANCEL_BUTTON:
				this.cancelBtn.setEnabled(enable);
				break;
			default:
				throw new IllegalArgumentException("Wrong Button Type.");
		}

	}

	@Override
	public void setButtonVisable(int button, boolean visable) {

		switch(button){
			case PageButtonPanel.BACK_BUTTON:
				this.backBtn.setEnabled(visable);
				break;
			case PageButtonPanel.NEXT_BUTTON:
				this.nextBtn.setEnabled(visable);
				break;
			case PageButtonPanel.FINISH_BUTTON:
				this.finishBtn.setEnabled(visable);
				break;
			case PageButtonPanel.CANCEL_BUTTON:
				this.cancelBtn.setEnabled(visable);
				break;
			default:
				throw new IllegalArgumentException("Wrong Button Type.");
		}
	}


	private void initComponents() {

		this.cancelBtn = new JButton();
		this.finishBtn = new JButton();
		this.nextBtn = new JButton();
		this.backBtn = new JButton();

		this.cancelBtn.setMnemonic('C');
		this.cancelBtn.setText("Cancel");

		this.finishBtn.setMnemonic('F');
		this.finishBtn.setText("Finish");

		this.nextBtn.setMnemonic('N');
		this.nextBtn.setText("Next >");

		this.backBtn.setMnemonic('B');
		this.backBtn.setText("< Back");

		GroupLayout layout = new GroupLayout(this);
		this.setLayout(layout);
		layout.setHorizontalGroup(
				layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
				.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
						.addContainerGap(143, Short.MAX_VALUE)
						.addComponent(this.backBtn)
						.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.nextBtn)
						.addGap(10, 10, 10)
						.addComponent(this.finishBtn)
						.addGap(10, 10, 10)
						.addComponent(this.cancelBtn)
						.addContainerGap())
		);
		layout.setVerticalGroup(
				layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
				.addGroup(layout.createSequentialGroup()
						.addContainerGap()
						.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
								.addComponent(this.cancelBtn)
								.addComponent(this.finishBtn)
								.addComponent(this.nextBtn)
								.addComponent(this.backBtn))
								.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
		);
	}

	private JButton backBtn;
	private JButton cancelBtn;
	private JButton finishBtn;
	private JButton nextBtn;
}
